from typing import Dict, Any, Optional, BinaryIO, Union
import os
import time

import httpx
from fastapi import UploadFile

from app.core.config import settings
from app.core.logger import logger
from app.core.exceptions import DifyAPIError
from app.models.audio import TtsVoice, AudioFormat

class AudioService:
    """
    音频服务类
    
    处理文字转语音和语音转文字功能
    """
    def __init__(self, api_key: Optional[str] = None):
        """
        初始化音频服务
        
        Args:
            api_key: 可选的API密钥，如果不提供则使用环境变量中的配置
        """
        self.base_url = settings.DIFY_API_BASE_URL
        self.api_key = api_key or settings.DIFY_API_KEY
        
        if not self.api_key:
            raise ValueError("API密钥未提供")
        
        self.headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
    
    async def text_to_speech(
        self, 
        text: Optional[str], 
        user: str,
        message_id: Optional[str] = None,
        voice: Union[TtsVoice, str] = TtsVoice.NEUTRAL,
        format: Union[AudioFormat, str] = AudioFormat.MP3,
        speed: float = 1.0
    ) -> Dict[str, Any]:
        """
        文字转语音
        
        Args:
            text: 需要转换为语音的文本，如果提供message_id可为None
            user: 用户ID
            message_id: 可选的消息ID，用于查找对应的文本内容
            voice: 语音类型
            format: 音频格式
            speed: 语速
            
        Returns:
            转换结果，包含音频文件数据或URL
        """
        # 准备请求数据 - 按官方文档要求格式
        data = {
            "user": user
        }
        
        # 如果提供了message_id，优先使用
        if message_id:
            data["message_id"] = message_id
        elif text:
            data["text"] = text
        else:
            raise ValueError("必须提供text或message_id参数")
        
        # 记录请求信息
        logger.debug(f"文字转语音: user={user}, text_length={len(text) if text else 0}, message_id={message_id}")
        
        # 设置请求头 - 严格按照官方文档格式
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json",
            "Accept": "audio/*, application/json"  # 同时接受音频和JSON
        }
        
        # 发送请求
        async with httpx.AsyncClient() as client:
            try:
                # 记录完整请求信息，便于调试
                logger.info(f"向Dify API发送文本转语音请求: URL={self.base_url}/text-to-audio")
                logger.debug(f"请求头: {headers}")
                logger.debug(f"请求数据: {data}")
                
                # 发送请求到Dify API，严格按照文档格式
                response = await client.post(
                    f"{self.base_url}/text-to-audio",
                    headers=headers,
                    json=data,
                    timeout=settings.TIMEOUT
                )
            
                # 处理错误响应
                if response.status_code >= 400:
                    error_data = {}
                    try:
                        if response.headers.get("content-type") == "application/json":
                            error_data = response.json()
                    except Exception as json_error:
                        logger.error(f"解析错误响应失败: {str(json_error)}")
                    
                    error_msg = error_data.get("message", response.text or "未知错误")
                    logger.error(f"文字转语音失败: 状态码={response.status_code}, 错误={error_msg}")
                    logger.debug(f"错误响应详情: {response.text}")
                    raise DifyAPIError(
                        status_code=response.status_code,
                        detail=f"文字转语音失败: {error_msg}",
                        error_data=error_data
                    )
                
                # 如果是音频数据，直接返回
                if response.headers.get("content-type", "").startswith("audio/"):
                    audio_content = response.content
                    content_type = response.headers.get("content-type", "audio/mp3")
                    
                    # 检查音频内容是否有效
                    if not audio_content or len(audio_content) < 100:
                        logger.warning(f"API返回的音频内容可能无效，大小: {len(audio_content) if audio_content else 0} 字节")
                    
                    logger.info(f"从API接收到音频内容，大小: {len(audio_content)} 字节，类型: {content_type}")
                    
                    return {
                        "audio_content": audio_content,
                        "content_type": content_type
                    }
                
                # 否则尝试解析JSON响应
                try:
                    json_data = response.json()
                    logger.info(f"收到JSON格式响应: {json_data}")
                    
                    # 如果包含audio_url字段，则返回
                    if "audio_url" in json_data:
                        return json_data
                        
                    # 检查是否有其他错误信息
                    if "error" in json_data or "detail" in json_data:
                        error_msg = json_data.get("error") or json_data.get("detail") or "API返回了错误信息"
                        logger.warning(f"API返回了错误: {error_msg}")
                        raise DifyAPIError(detail=error_msg)
                    
                    return json_data
                except Exception as e:
                    logger.warning(f"解析API响应失败: {str(e)}")
                    
                    # 尝试直接返回响应内容作为音频
                    return {
                        "audio_content": response.content,
                        "content_type": "audio/mp3"  # 默认MP3
                    }
                    
            except httpx.RequestError as req_error:
                logger.error(f"文字转语音请求错误: {str(req_error)}")
                raise DifyAPIError(
                    status_code=500,
                    detail=f"文字转语音请求错误: {str(req_error)}"
                )
    
    async def speech_to_text(
        self, 
        audio_file: UploadFile, 
        user: str
    ) -> Dict:
        """
        将语音转换为文本
        
        Args:
            audio_file: 上传的音频文件
            user: 用户ID
            
        Returns:
            包含转换文本的字典
        """
        try:
            # 检查文件大小
            file_size = 0
            content = await audio_file.read()
            file_size = len(content)
            
            # 重置文件指针，以便后续处理
            await audio_file.seek(0)
            
            # 文件大小限制(15MB)
            if file_size > 15 * 1024 * 1024:
                raise ValueError(f"文件大小超过限制：{file_size / (1024 * 1024):.2f}MB > 15MB")
            
            # 确保文件类型正确
            content_type = audio_file.content_type
            if not content_type or not content_type.startswith('audio/'):
                # 如果Content-Type不是音频类型，尝试从文件名中获取
                ext = audio_file.filename.split('.')[-1].lower() if audio_file.filename else ""
                content_type_map = {
                    'mp3': 'audio/mp3',
                    'mp4': 'audio/mp4',
                    'mpeg': 'audio/mpeg',
                    'mpga': 'audio/mpeg',
                    'm4a': 'audio/m4a',
                    'wav': 'audio/wav',
                    'webm': 'audio/webm'
                }
                content_type = content_type_map.get(ext, 'audio/mp3')
                logger.info(f"从文件扩展名获取Content-Type: {ext} -> {content_type}")
            
            # 调用具体的语音识别服务
            logger.info(f"开始处理语音转文字请求: 文件名={audio_file.filename}, 大小={file_size/1024:.2f}KB, 类型={content_type}")
            result = await self._call_speech_recognition_service(content, user, audio_file.filename, content_type)
            
            # 确保结果中包含text字段
            if "text" not in result:
                logger.warning("语音识别结果中没有text字段")
                result["text"] = ""
            
            logger.info(f"语音转文字完成: 文本长度={len(result.get('text', ''))}")
            return result
                
        except httpx.TimeoutException as e:
            error_msg = f"语音转文字请求超时: {str(e)}"
            logger.error(error_msg)
            raise DifyAPIError(detail=error_msg)
            
        except httpx.RequestError as e:
            error_msg = f"语音转文字网络错误: {str(e)}"
            logger.error(error_msg)
            raise DifyAPIError(detail=error_msg)
            
        except ValueError as e:
            # 参数验证错误，直接抛出，不包装为DifyAPIError
            logger.error(f"语音转文字参数错误: {str(e)}")
            raise
            
        except Exception as e:
            error_msg = f"语音转文字异常: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise DifyAPIError(detail=error_msg)
    
    async def _call_speech_recognition_service(self, audio_content: bytes, user: str, filename: str = None, content_type: str = 'audio/wav') -> Dict:
        """调用具体的语音识别服务
        
        Args:
            audio_content: 音频内容
            user: 用户ID
            filename: 文件名
            content_type: 内容类型
            
        Returns:
            语音识别结果
        """
        try:
            # 准备请求数据
            if not filename:
                filename = f"audio_{user}_{int(time.time())}.{content_type.split('/')[-1]}"
            
            # 修正：使用正确的API端点
            api_endpoint = "/audio-to-text"  # 之前错误使用 "/audio/transcriptions"
            
            logger.info(f"向Dify API发送请求: URL={self.base_url}{api_endpoint}, API_KEY={self.api_key[:5]}***")
            
            # 准备文件和表单数据，格式与测试脚本一致
            files = {
                "file": (filename, audio_content, content_type)
            }
            form_data = {"user": user}
            
            headers = {
                "Authorization": f"Bearer {self.api_key}",
                # 不设置Content-Type，httpx会自动设置multipart/form-data
            }
            
            logger.info(f"发送语音识别请求: user={user}, content_length={len(audio_content)}, content_type={content_type}")
            
            try:
                # 调用外部API
                async with httpx.AsyncClient() as client:
                    logger.info(f"开始调用语音识别服务: {self.base_url}{api_endpoint}")
                    
                    try:
                        # 发送请求
                        response = await client.post(
                            f"{self.base_url}{api_endpoint}",
                            headers=headers,
                            files=files,
                            data=form_data,
                            timeout=settings.TIMEOUT
                        )
                        
                        # 记录响应状态和内容类型
                        logger.info(f"语音识别响应: status_code={response.status_code}, content_type={response.headers.get('content-type')}")
                        
                        # 处理错误响应
                        if response.status_code >= 400:
                            error_msg = "未知错误"
                            try:
                                if response.headers.get("content-type") == "application/json":
                                    error_data = response.json()
                                    error_msg = error_data.get("detail") or error_data.get("message") or f"状态码: {response.status_code}"
                                else:
                                    error_msg = f"状态码: {response.status_code}, 响应内容: {response.text[:200] if response.text else '空响应'}"
                            except Exception as parse_error:
                                error_msg = f"状态码: {response.status_code}, 解析错误: {str(parse_error)}, 响应内容: {response.text[:100] if response.text else '空响应'}..."
                            
                            logger.error(f"语音识别服务调用失败: {error_msg}")
                            raise DifyAPIError(
                                status_code=response.status_code,
                                detail=f"语音识别服务调用失败: {error_msg}"
                            )
                        
                        # 解析成功响应
                        try:
                            if response.headers.get("content-type") == "application/json":
                                result = response.json()
                                logger.info(f"语音识别成功: {result}")
                                return result
                            else:
                                # 如果不是JSON响应但状态码是成功的，尝试处理文本响应
                                logger.warning(f"API返回了非JSON响应: {response.text[:100]}...")
                                return {"text": response.text or ""}
                        except Exception as parse_error:
                            logger.error(f"解析响应失败: {str(parse_error)}")
                            return {"text": response.text or "", "parse_error": str(parse_error)}
                        
                    except Exception as request_error:
                        logger.error(f"调用语音识别服务异常: {str(request_error)}", exc_info=True)
                        raise DifyAPIError(detail=f"语音识别服务请求异常: {str(request_error)}")
                    
            except Exception as e:
                logger.error(f"执行请求异常: {str(e)}", exc_info=True)
                raise DifyAPIError(detail=f"语音识别服务执行异常: {str(e)}")
                
        except DifyAPIError as api_error:
            # 直接向上传递DifyAPIError异常，确保有详细信息
            if not getattr(api_error, 'detail', None):
                api_error.detail = "语音识别服务API错误(无详细信息)"
            raise api_error
        except Exception as e:
            # 捕获所有其他异常
            error_msg = f"语音识别服务调用异常: {str(e)}"
            logger.error(error_msg, exc_info=True)
            raise DifyAPIError(detail=error_msg)
    
    async def get_audio_task_status(self, task_id: str) -> Dict[str, Any]:
        """
        获取音频任务状态
        
        Args:
            task_id: 任务ID
            
        Returns:
            任务状态信息
        """
        logger.debug(f"获取音频任务状态: task_id={task_id}")
        
        async with httpx.AsyncClient() as client:
            response = await client.get(
                f"{self.base_url}/audio/tasks/{task_id}",
                headers=self.headers,
                timeout=settings.TIMEOUT
            )
            
            # 处理错误响应
            if response.status_code >= 400:
                error_data = response.json() if response.headers.get("content-type") == "application/json" else {}
                error_msg = error_data.get("message", "未知错误")
                logger.error(f"获取音频任务状态失败: {error_msg}")
                raise DifyAPIError(
                    status_code=response.status_code,
                    detail=f"获取音频任务状态失败: {error_msg}",
                    error_data=error_data
                )
            
            # 返回成功响应
            return response.json()
